package com.ztc.common.alipay;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.math.BigDecimal;
import java.util.Map;

import static com.ztc.common.alipay.config.AlipayConfig.*;


/**
 * 调起支付宝支付类.
 */
public final class Alipay {

    private static final Logger LOGGER = LogManager.getLogger(com.ztc.common.alipay.Alipay.class);

    private Alipay() {
    }

    //实例化客户端
    private static AlipayClient alipayClient = new DefaultAlipayClient(ALIPAY_URL, APP_ID, APP_PRIVATE_KEY, FORMAT, CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE);

    /**
     * 创建APP支付宝支付
     *
     * @param body           对一笔交易的具体描述信息。如果是多种商品，请将商品描述字符串累加传给body。
     * @param subject        商品的标题/交易标题/订单标题/订单关键字等。
     * @param outTradeNo     商户网站唯一订单号
     * @param timeOutExpress 该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天，1c-当天. 该参数数值不接受小数点， 如 1.5h，可转换为 90m。
     * @param totalAmout     订单总金额，单位为元，精确到小数点后两位，取值范围[0.01,100000000]
     * @param passbackParams 公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
     * @return 支付参数, 直接返回给客户端使用
     */
    public static String createAliPay(String body, String subject, String outTradeNo, String timeOutExpress, String totalAmout, String passbackParams) {
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        //对一笔交易的具体描述信息。如果是多种商品，请将商品描述字符串累加传给body。
        model.setBody(body);
        //商品的标题/交易标题/订单标题/订单关键字等。
        model.setSubject(subject);
        //商户网站唯一订单号
        model.setOutTradeNo(outTradeNo);
        //该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天，1c-当天. 该参数数值不接受小数点， 如 1.5h，可转换为 90m。
        if (timeOutExpress != null) {
            model.setTimeoutExpress(timeOutExpress);
        }
        //订单总金额，单位为元，精确到小数点后两位，取值范围[0.01,100000000]
        model.setTotalAmount(totalAmout);
        //公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
        model.setPassbackParams(passbackParams);
        //销售产品码，商家和支付宝签约的产品码，为固定值QUICK_MSECURITY_PAY
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        //支付通知地址
        request.setNotifyUrl(NOTIFY_PAY_URL);
        String params = null;
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            params = response.getBody();//就是orderString 可以直接给客户端请求，无需再做处理。
            LOGGER.debug("发起支付宝支付参数",params);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return params;
    }

    /**
     * 支付宝回调进行SDK验证签名
     * @param resultMap 支付宝回调信息
     * @param outTradeNo 商户订单号
     * @param totalAmount 订单金额
     * @return 验证是否通过
     */
    public static Boolean checkAliPayOrder(Map<String, String> resultMap,String outTradeNo,BigDecimal totalAmount){
        boolean signVerified = false; //调用SDK验证签名
        try {
            signVerified = AlipaySignature.rsaCheckV1(resultMap, ALIPAY_PUBLIC_KEY, CHARSET);
        } catch (AlipayApiException e) {
            LOGGER.debug("支付宝返回SDK校验失败信息:",e.getErrMsg());
        }
        if(signVerified){
            // 验签成功后 进行二次校验
            if(!outTradeNo.equals(resultMap.get("out_trade_no"))){
                return false;
            }
            if(totalAmount.compareTo(new BigDecimal(resultMap.get("total_amount"))) != 0){
                return false;
            }
            if(!APP_ID.equals(resultMap.get("app_id"))){
                return false;
            }
            return true;
        }else{
            // 验签失败则记录异常日志，并在response中返回failure.
            return false;
        }
    }

    /**
     * @param outTradeNo   订单支付时传入的商户订单号,不能和 trade_no同时为空。
     * // @param tradeNo      支付宝交易号，和商户订单号不能同时为空
     * @param refundAmount 需要退款的金额，该金额不能大于订单金额,单位为元，支持两位小数
     * @param outRequestNo 标识一次退款请求，同一笔交易多次退款需要保证唯一，如需部分退款，则此参数必传。
     * @param refundReason 退款的原因说明
     * @return
     */
    public static Boolean refundAliPay(String outTradeNo, String refundAmount, String outRequestNo, String refundReason) {
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.refund
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        //订单支付时传入的商户订单号,不能和 trade_no同时为空。
        model.setOutTradeNo(outTradeNo);
        //支付宝交易号，和商户订单号不能同时为空
        //model.setTradeNo(tradeNo);
        //需要退款的金额，该金额不能大于订单金额,单位为元，支持两位小数
        model.setRefundAmount(refundAmount);
        //退款的原因说明
        model.setRefundReason(refundReason);
        //标识一次退款请求，同一笔交易多次退款需要保证唯一，如需部分退款，则此参数必传。
        model.setOutRequestNo(outRequestNo);
        request.setBizModel(model);
        //支付通知地址
        request.setNotifyUrl(NOTIFY_PAY_URL);
        Boolean isTrue = false;
        try {
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            LOGGER.debug("退款返回信息",response.getBody());
            if (response.isSuccess()) {
                isTrue = true;
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return isTrue;
    }
}